﻿using System;
using System.Collections;
using System.Text;

using CookComputing.XmlRpc;
using System.Net;

namespace HellaDotNet.XmlRPC
{
    public class ConnectionStatusEventArgs : EventArgs
    {
        private Connection.ConnectionStatus _ConnectionStatus;

        public ConnectionStatusEventArgs(Connection.ConnectionStatus status) { this.ConnectionStatus = status; }

        public Connection.ConnectionStatus ConnectionStatus { get { return this._ConnectionStatus; } set { this._ConnectionStatus = value; } }
    }

    public delegate void ConnectionStatusChangedDelegate(object sender, ConnectionStatusEventArgs e);

    public class Connection
    {
        public enum ConnectionStatus
        {
            Success,
            Busy,
            Failed
        }

        public event ConnectionStatusChangedDelegate ConnectionStatusChanged;

        private IHellanzb _Hellanzb;

        public Connection(string url)
        {
            this._Hellanzb = XmlRpcProxyGen.Create<IHellanzb>();
            this._Hellanzb.Url = url;
        }

        public Connection(string url, string username, string password)
            : this(url)
        {
            this._Hellanzb.Credentials = new NetworkCredential(username, password);
        }

        private void ChangeConnectionStatus(ConnectionStatus status)
        {
            this.ConnectionStatusChanged(this, new ConnectionStatusEventArgs(status));
        }

        public Methods GetMethods()
        {
            this.ChangeConnectionStatus(ConnectionStatus.Busy);

            Methods result = new Methods();

            try
            {
                Array methods = this._Hellanzb.ListMethods();

                foreach (string m in methods)
                {
                    result.Add(new Method(m, this._Hellanzb.MethodHelp(m)));
                }

                this.ChangeConnectionStatus(ConnectionStatus.Success);
            }
            catch
            {
                this.ChangeConnectionStatus(ConnectionStatus.Failed);
            }

            return result;
        }

        private Status ParseStatusStruct(XmlRpcStruct status)
        {
            Status result = new Status();

            //Log Entries
            LogEntries logentries = new LogEntries();

            object[] log = (object[])status["log_entries"];
            for (int i = 0; i < log.Length; i++)
            {
                foreach (DictionaryEntry de in (Hashtable)log[i])
                {
                    logentries.Add(new LogEntry((LogEntryType)Enum.Parse(typeof(LogEntryType), de.Key.ToString(), true), de.Value.ToString()));
                }
            }
            result.LogEntries = logentries;


            object[] nzbs;
            Hashtable nzb;

            //Currently downloading
            nzbs = (object[])status["currently_downloading"];
            if (nzbs.Length > 0)
            {
                for (int i = 0; i < nzbs.Length; i++)
                {
                    nzb = (Hashtable)nzbs[i];
                    result.NzbFiles.Add(new NzbFile(Convert.ToInt32(nzb["id"]), nzb["nzbName"].ToString(), Convert.ToInt32(nzb["total_mb"]), (Convert.ToBoolean(nzb["is_par_recovery"]) ? NzbFile.NzbFileStatus.DownloadingPars : NzbFile.NzbFileStatus.Downloading)));
                }                
            }

            //Currently processing
            nzbs = (object[])status["currently_processing"];
            if (nzbs.Length > 0)
            {
                for (int i = 0; i < nzbs.Length; i++)
                {
                    nzb = (Hashtable)nzbs[i];
                    result.NzbFiles.Add(new NzbFile(Convert.ToInt32(nzb["id"]), nzb["nzbName"].ToString(), Convert.ToInt32(nzb["total_mb"]), NzbFile.NzbFileStatus.Processing));
                }
            }

            //Queued files
            nzbs = (object[])status["queued"];
            if (nzbs.Length > 0)
            {
                for (int i = 0; i < nzbs.Length; i++)
                {
                    nzb = (Hashtable)nzbs[i];
                    result.NzbFiles.Add(new NzbFile(Convert.ToInt32(nzb["id"]), nzb["nzbName"].ToString(), Convert.ToInt32(nzb["total_mb"]), NzbFile.NzbFileStatus.Queued));
                }
            }

            result.Time = DateTime.Parse(status["time"].ToString());
            result.PercentageCompleted = Convert.ToInt32(status["percent_complete"].ToString());
            result.Version = status["version"].ToString();
            result.ConfigFile = status["config_file"].ToString();
            result.TotalDownloadedFiles = Convert.ToInt32(status["total_dl_files"].ToString());
            result.Eta = TimeSpan.FromSeconds(Convert.ToDouble(status["eta"]));
            result.DownloadRate = Convert.ToInt32(status["rate"]);
            result.QueuedMB = Convert.ToInt32(status["queued_mb"]);
            result.Hostname = status["hostname"].ToString();
            result.Uptime = status["uptime"].ToString();
            result.TotalDownloadedSegments = Convert.ToInt32(status["total_dl_segments"]);
            result.MaximumDownloadRate = Convert.ToInt32(status["maxrate"]);
            result.TotalDownloadedNzbs = Convert.ToInt32(status["total_dl_nzbs"]);
            result.TotalDownloadedMBs = Convert.ToInt32(status["total_dl_mb"]);
            result.IsPaused = Convert.ToBoolean(status["is_paused"]);

            return result;
        }

        public Status GetStatus()
        {
            this.ChangeConnectionStatus(ConnectionStatus.Busy);

            Status result = new Status();

            try
            {
                result = this.ParseStatusStruct(this._Hellanzb.Status());
                this.ChangeConnectionStatus(ConnectionStatus.Success);
            }
            catch
            {
                this.ChangeConnectionStatus(ConnectionStatus.Failed);
            }

            return result;
        }

        public Status Pause()
        {
            this.ChangeConnectionStatus(ConnectionStatus.Busy);

            Status result = new Status();

            try
            {
                result = this.ParseStatusStruct(this._Hellanzb.Pause());
                this.ChangeConnectionStatus(ConnectionStatus.Success);
            }
            catch
            {
                this.ChangeConnectionStatus(ConnectionStatus.Failed);
            }

            return result;
        }

        public Status Continue()
        {
            this.ChangeConnectionStatus(ConnectionStatus.Busy);

            Status result = new Status();

            try
            {
                result = this.ParseStatusStruct(this._Hellanzb.Continue());
                this.ChangeConnectionStatus(ConnectionStatus.Success);
            }
            catch
            {
                this.ChangeConnectionStatus(ConnectionStatus.Failed);
            }

            return result;
        }

        public NzbFiles Move(Int32 id, Int32 pos)
        {
            NzbFiles result = new NzbFiles();
            Hashtable nzb;

            try
            {
                object[] nzbs = this._Hellanzb.Move(id, pos);
                
                if (nzbs.Length > 0)
                {
                    for (int i = 0; i < nzbs.Length; i++)
                    {
                        nzb = (Hashtable)nzbs[i];
                        result.Add(new NzbFile(Convert.ToInt32(nzb["id"]), nzb["nzbName"].ToString(), Convert.ToInt32(nzb["total_mb"]), (Convert.ToBoolean(nzb["is_par_recovery"]) ? NzbFile.NzbFileStatus.DownloadingPars : NzbFile.NzbFileStatus.Queued)));
                    }
                }

                return result;
            }
            catch
            {
                this.ChangeConnectionStatus(ConnectionStatus.Failed);
            }

            return result;
        }

        public Status Force(Int32 id)
        {
            this.ChangeConnectionStatus(ConnectionStatus.Busy);

            Status result = new Status();

            try
            {
                result = this.ParseStatusStruct(this._Hellanzb.Force(id));
                this.ChangeConnectionStatus(ConnectionStatus.Success);
            }
            catch
            {
                this.ChangeConnectionStatus(ConnectionStatus.Failed);
            }

            return result;
        }
    }
}